#include "manager.h"
#include <QDateTime>
#include <QDebug>
#include <QHostAddress>
#include <QJsonArray>
#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonValue>
#include <QList>
#include <QMap>
#include <QTcpSocket>

class Request {
public:
  const QString &roomName() const { return m_roomName; }
  const QString &nick() const { return m_nick; }

  bool cerater() { return req.value("self").toBool(); }

  int alive() { return req.value("alive").toInt(); }

  int undercover() { return req.value("undercover").toInt(); }

  QString other() { return req.value("other").toString(); }
  QString msg() { return req.value("msg").toString(); }

  Request(QJsonObject &req) {
    m_roomName = req.value("room").toString();
    m_nick = req.value("name").toString();
    m_type = req.value("type").toString();
    this->req = req;
  }

  bool isType(const QString &type) { return m_type == type; }

  QByteArray response(const QString &type) {
    QJsonObject obj;
    obj["code"] = code;
    obj["msg"] = m_msg;
    obj["type"] = type;
    obj["data"] = res;

    QJsonDocument doc(obj);
    return doc.toJson(QJsonDocument::Compact);
  }

  bool error(const QString &info) {
    code = 1;
    m_msg = info;
    return true;
  }

private:
  QString m_msg = "sucess";
  int code = 0;
  QJsonValue res;
  QJsonObject req;

  QString m_type;
  QString m_roomName;
  QString m_nick;
};

Manager::Manager(QObject *parent) : QObject(parent) {}

Manager::~Manager() {}

void Manager::addClient(QTcpSocket *client) {
  client->setProperty("date", QDateTime::currentDateTimeUtc());
  connect(client, SIGNAL(disconnected()), this, SLOT(disconnectedHandle()));
  connect(client, SIGNAL(readyRead()), this, SLOT(readyReadHandle()));
  qInfo() << (QString("new connect ip: %1 port: %2")
                  .arg(client->peerAddress().toString())
                  .arg(client->peerPort()));
}

void Manager::addRoom(GameRoom *room) { m_rooms[room->roomName()] = room; }

bool Manager::existRoom(const QString &roomName) {
  return m_rooms.contains(roomName);
}

GameRoom *Manager::takeRoom(const QString &roomName) {
  return m_rooms.take(roomName);
}

GameRoom *Manager::room(const QString &roomName) {
  return m_rooms.value(roomName, nullptr);
}

void Manager::disconnectedHandle() {
  QTcpSocket *client = dynamic_cast<QTcpSocket *>(this->sender());
  User *user = (User *)client->userData(0);
  user->leave = true;
  m_clients.removeOne(client);
}

void Manager::readyReadHandle() {
  QTcpSocket *client = dynamic_cast<QTcpSocket *>(this->sender());
  QByteArray bytes = client->readAll();
  qDebug() << QString("[IP : %1 PORT: %2] ")
                  .arg(client->peerAddress().toString())
                  .arg(client->peerPort())
           << QString(bytes).simplified();
  QJsonDocument doc = QJsonDocument::fromJson(bytes);
  if (!doc.isObject()) {
    qCritical() << __LINE__ << __FUNCTION__ << "data type error." << doc;
    return;
  }
  QJsonObject object = doc.object();
  QString type = object.value("type").toString();
  Request request(object);
  bool ret = false;
  if (request.isType("enter")) {
    ret = joinRoomHandler(request, client);
  } else if (request.isType("ticket")) {
    ret = voteHandler(request);
  } else if (request.isType("talk")) {
    ret = talkHandler(request);
  } else if (request.isType("play")) {
    ret = playGameHandler(request);
  } else if (request.isType("leave")) {
    ret = leaveHandler(request);
  } else {
    qDebug() << "[CLOSE]unknow handle";
    client->close();
  }
  if (ret) {
    //        QJsonObject obj;
    //        obj["code"] = code;
    //        obj["msg"] = m_msg;
    //        obj["type"] = type;
    //        obj["data"] = res;

    //        QJsonDocument doc(obj);
    //        doc.toJson(QJsonDocument::Compact);
    writeSocket(client, request.response(type));
    client->flush();
  }
}

bool Manager::joinRoomHandler(Request &request, QTcpSocket *client) {

  bool created = request.cerater();
  GameRoom *room = this->room(request.roomName());
  qDebug() << "=============" << created << room;
  if (created && room != nullptr) {
    return request.error("房间已经存在，不能在次创建！");
  } else if (!created && room == nullptr) {
    return request.error("该房间不存在，请先创建房间！");
  }
  if (room != nullptr && room->exist(request.nick())) {
    return request.error("该用户已存在，请更换昵称！");
  }
  if (room == nullptr) {
    room = new GameRoom(request.roomName());
    addRoom(room);
  }

  User *user = (User *)client->userData(0);
  if (user == nullptr) {
    user = new User;
    user->sock = client;
    user->sock->setUserData(0, user);
    room->join(user);
  }
  user->room = request.roomName();
  user->nick = request.nick();
  user->created = created;
  room->syncNotify();
  return true;
}

bool Manager::playGameHandler(Request &request) {
  GameRoom *room = this->room(request.roomName());
  if (room == nullptr)
    return request.error("开始不了，房间不存在！");

  room->setNumber(request.alive(), request.undercover());
  room->allocWord();
  room->notify("play", "Begin Game");
  room->say(0);
  return false;
}

bool Manager::leaveHandler(Request &request) {
  GameRoom *room = this->room(request.roomName());
  if (room == nullptr)
    return request.error("发生异常，房间不存在！");
  User *user = room->user(request.nick());
  if (user == nullptr)
    return request.error("发生异常，用户不存在！");

  if (user->created) { // delete room
    room = takeRoom(request.roomName());
    room->notify("disperse");
    delete room;
  } else { // delete user
    room->leave(user);
  }
  room->syncNotify();
  return false;
}

bool Manager::voteHandler(Request &request) {
  GameRoom *room = this->room(request.roomName());
  if (room == nullptr)
    return request.error("发生异常，房间不存在！");
  // 接收投票信息
  QString other = request.other();
  room->vote(request.nick(), other);
  return false;
}

bool Manager::talkHandler(Request &request) {
  GameRoom *room = this->room(request.roomName());
  if (room == nullptr)
    return request.error("发生异常，房间不存在！");
  // 转发消息
  room->notify("say",
               QString("[%1]: %2").arg(request.nick()).arg(request.msg()));
  room->say();
  return false;
}
